﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;

namespace SharePointEnforcer.Rules
{
    public interface iEnforcerRule
    {
        void Evaluate(EnforcerArg pArg);
        void Repair(EnforcerArg pArg);
    }


    public abstract class EnforcerRuleBase : iEnforcerRule
    {
        public abstract void Evaluate(EnforcerArg pArg);

        public virtual void Repair(EnforcerArg pArg)
        {
            /* do nothing */
        }
    }


    public class EnforcerArg: IDisposable
    {
        public EnforcerArg(SPWeb pWeb)
        {
            InitVars(pWeb);
        }


        protected void InitVars(SPWeb pWeb)
        {
            Web = pWeb;
            Site = pWeb.Site;
            Result = EnforcerEvaluateResult.Ignore;
            RepairState = EnforcerRepairResult.NoRepairRequired;
            
            IsPublishingWeb = PublishingWeb.IsPublishingWeb(pWeb);
            if (IsPublishingWeb) PublishWeb = PublishingWeb.GetPublishingWeb(pWeb);

            this.IsRootWeb = pWeb.IsRootWeb;
            this.CustomProperties = new EnforcerArgProperties();
        }


        #region public properties

        public SPSite Site { get; protected set; }
        public SPWeb Web { get; protected set; }
        public PublishingWeb PublishWeb { get; protected set; }

        public bool IsPublishingWeb { get; protected set; }
        public bool IsRootWeb { get; protected set; }

        /// <summary>
        /// Any Rule can define whatever custom properties it may need to here.  This is intended to avoid duplication of effort between the Identification of 
        /// a rule violation in Evaluate() and its subsequent Repair() by allowing key values to be passed between the two methods
        /// </summary>
        public EnforcerArgProperties CustomProperties { get; protected set; }

        #endregion

        public EnforcerRepairResult RepairState { get; set; }
        public EnforcerEvaluateResult Result { get; set; }


        public void Dispose()
        {
            if (this.CustomProperties != null) this.CustomProperties.Dispose();
            this.CustomProperties = null;

            // We are not creating the objects below so we cannot .Dispose of them.  We can explicitly destroy this reference to them though.
            this.Site = null;
            this.Web = null;
            this.PublishWeb = null;
        }
    }


    public class EnforcerArgProperties : Dictionary<string, object>, IDisposable
    {
        public void Dispose()
        {
            foreach (string key in this.Keys)
            {
                //~~ Need to test that the .Dispose is getting called properly on disposable objects
                this.DoDispose(this[key]);
                this.Remove(key);
            }
        }

        private void DoDispose(IDisposable value) { value.Dispose(); }
        private void DoDispose(object value) { /* do nothing */ }
    }


    public enum EnforcerEvaluateResult : int
    {
        Ignore = 0,
        Pass = 1,
        Fail = 2
    }

    public enum EnforcerRepairResult : int
    {
        NoRepairRequired = 0,
        RepairRequired = 1,
        RepairCompleted = 2,
        RepairFailed = 3
    }
}
